import net
import request
import modem
import osTimer
import ql_fs
from misc import ADC
from machine import Pin
from gnss import GnssGetData
from usr.qframe import CurrentApp
from usr.qframe.scheduler import Scheduler
from usr.qframe.threading import Lock
from usr.qframe.common import WakeLock
from usr.qframe.logging import getLogger
from usr.qframe.datetime import DateTime


logger = getLogger(__name__)

scheduler = Scheduler()


class History(object):
    history_file_path = '/usr/history.db'

    def __init__(self):
        self.db = {'data': []}
        self.lock = Lock()

    def __enter__(self):
        self.lock.acquire()
        if not ql_fs.path_exists(self.history_file_path):
            ql_fs.touch(self.history_file_path, self.db)
        self.db.update(ql_fs.read_json(self.history_file_path))
        return self

    def __exit__(self, *args, **kwargs):
        ql_fs.touch(self.history_file_path, self.db)
        self.db = {'data': []}
        self.lock.release()

    def append(self, raw, user_data=None):
        if len(self.db['data']) >= 30:
            return
        self.db['data'].append({'raw': raw, 'user_data': user_data or {}})


class GnssFunction(object):
    lock = Lock()

    def __init__(self):
        self.gnss = GnssGetData(4, 9600, 8, 0, 1, 0)
        self.gnss_standby_control_pin = Pin(Pin.GPIO4, Pin.OUT, Pin.PULL_DISABLE, 1)
        self.timer = osTimer()

    def __getattr__(self, item):
        return getattr(self.gnss, item)

    def active(self):
        if self.gnss_standby_control_pin.read() == 0:
            self.gnss_standby_control_pin.write(1)
            logger.debug('{} activate...'.format(type(self).__name__))
        self.timer.start(10000, 0, lambda _: self.standby())

    def standby(self):
        if self.gnss_standby_control_pin.read() == 1:
            self.gnss_standby_control_pin.write(0)
            logger.debug('{} standby...'.format(type(self).__name__))

    def get_location(self):
        with self.lock:
            time_point_start = DateTime.now()
            self.active()
            result = gnss.read_gnss_data(max_retry=3, debug=0)
            if result == 0:
                logger.error('no gnss nmea data read')
                return
            if not gnss.isFix():
                logger.debug('gnss not fixed')
                return
            result = gnss.getLocation()
            if result == -1:
                logger.error('gnss getLocation failed')
                return
            time_point_end = DateTime.now()
            longitude, lon_direction, latitude, lat_direction = result
            return dict(
                imei=modem.getDevImei(),
                # (117.1156448333333, 'E', 31.82134916666667, 'N')
                lat='{:0.6f}'.format(latitude),
                lng='{:0.6f}'.format(longitude),
                speed='{:0.2f}'.format(gnss.getSpeed()),
                altitude='{:0.2f}'.format(gnss.getGeodeticHeight()),
                degrees='{:0.2f}'.format((time_point_end - time_point_start).total_seconds()),
                battery="{:0.2f}".format(adc.read(ADC.ADC0) / 1000 / 0.28),
                trigger_type="01",
                strength='{:02d}'.format(net.csqQueryPoll()),
                date="{}{:02d}{:02d}".format(str(time_point_end.year)[2:], time_point_end.month, time_point_end.day),
                time="{:02d}{:02d}{:02d}".format(time_point_end.hour, time_point_end.minute, time_point_end.second),
                net='G',
            )


history = History()
gnss = GnssFunction()
adc = ADC()  # ADC0:VBAT=0.28:1
adc.open()
gpio3 = Pin(Pin.GPIO3, Pin.OUT, Pin.PULL_DISABLE, 1)
gpio28 = Pin(Pin.GPIO28, Pin.OUT, Pin.PULL_DISABLE, 0)


global_wakeup_lock = WakeLock('job_wakeup_lock')


def post_http_data(raw):
    data = '&'.join(('{}={}'.format(k, v) for k, v in raw.items()))
    # logger.debug('data: {}'.format(data))
    try:
        resp = request.post(
            'https://217.160.133.55/API/insertLocation_TEC2.php',
            headers={'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'},
            data=data
        )
        status_code = resp.status_code
        logger.debug('post_http_data get resp POST status code: {}; data: {}'.format(status_code, data))
        resp.close()
    except Exception as e:
        logger.debug('post_http_data request post get exception: {}'.format(e))
        return False
    else:
        return status_code == 200


def post_mqtt_data(raw):
    data = {
        "msg_id": 10000001,
        "version": "1.0",
        "sensors": {
            "temperatrue": 30,
            "humidity": 30,
            "power": 4.0,
            "strength": 99,
            "net_type": 1,
            "cds": 90
        },
        "gps": {
            "longitude": raw['lng'],
            "latitude": raw['lat'],
            "speed": raw['speed'],
            "alititude": raw['altitude'],
            "degrees": raw['degrees'],
            "date": raw['date'],
            "time": "{}:{}:{}".format(*(raw['time'][i:i+2] for i in range(0, len(raw['time']), 2)))
        },
        "connection": {
            "state": "online",
            "timestamp": raw['date'] + raw['time'],
        }
    }
    try:
        resp_data = CurrentApp().mqtt_client_agent.client.publish_data(data)
        logger.debug('post_mqtt_data got resp data: {}'.format(resp_data))
        if resp_data is None or resp_data['update_reply']['result'] != 'success':
            return False
    except Exception as e:
        logger.error('post_mqtt_data got exception: {}'.format(e))
        return False
    return True


@scheduler.task(title='report_history', interval=300)
def report_history():
    logger.debug('report_history')
    with global_wakeup_lock:
        with history:
            remove_index_list = []
            for item in history.db['data']:
                raw = item['raw']
                flag = item['user_data']['flag']
                logger.debug('report_history raw: {}'.format(raw))
                if flag & 0x01 and post_http_data(raw):
                    logger.debug('report history post_http_data success')
                    flag &= ~0x01
                if flag & 0x02 and post_mqtt_data(raw):
                    logger.debug('report history post_mqtt_data success')
                    flag &= ~0x02
                if flag:
                    item['user_data']['flag'] = flag
                else:
                    remove_index_list.append(item)
            for item in remove_index_list:
                history.db['data'].remove(item)


@scheduler.task(title='report_location', interval=60)
def report_location():
    logger.debug('report_location')
    with global_wakeup_lock:
        with history:
            try:
                raw = gnss.get_location()
                if not raw:
                    return
                flag = 0
                if not post_http_data(raw):
                    logger.debug('report_location.post_http_data failed')
                    flag |= 0x01
                if not post_mqtt_data(raw):
                    logger.debug('report_location.post_mqtt_data failed')
                    flag |= 0x02
                if flag:
                    history.append(raw, user_data={'flag': flag})
            except Exception as e:
                logger.error('scheduler task report_location run error: {}'.format(e))


class JobService(object):

    def __init__(self, app=None):
        self.scheduler = scheduler
        if app:
            self.init_app(app)

    def init_app(self, app):
        app.register('job_service', self)

    def load(self):
        logger.info('load {}'.format(type(self).__name__))
        self.scheduler.start()

    @staticmethod
    def report_co_event():
        logger.debug('report_co_event')
        with global_wakeup_lock:
            try:
                raw = gnss.get_location()
                if not raw:
                    return
                data = {
                    "msg_id": 10000001,
                    "version": "1.0",
                    "alarm": [{"type": "co", "state": 1}],
                    "sensors": {
                        "temperatrue": 30,
                        "humidity": 30,
                        "power": 4.0,
                        "strength": 99,
                        "net_type": 1,
                        "cds": 90
                    },
                    "gps": {
                        "longitude": raw['lng'],
                        "latitude": raw['lat'],
                        "speed": raw['speed'],
                        "alititude": raw['altitude'],
                        "degrees": raw['degrees'],
                        "date": raw['date'],
                        "time": "{}:{}:{}".format(*(raw['time'][i:i + 2] for i in range(0, len(raw['time']), 2)))
                    },
                    "connection": {
                        "state": "online",
                        "timestamp": raw['date'] + raw['time'],
                    }
                }
                CurrentApp().mqtt_client_agent.client.publish_event(data)
            except Exception as e:
                logger.error('scheduler task report_location run error: {}'.format(e))
